package jmine.tec.hydra.dao;

import java.util.List;

import jmine.tec.hydra.api.HydraRequest;
import jmine.tec.hydra.api.HydraSpecification;
import jmine.tec.hydra.domain.HydraRequestImpl;
import jmine.tec.hydra.domain.HydraSystem;
import jmine.tec.hydra.domain.StatusHydraRequest;
import jmine.tec.hydra.domain.enumx.HydraRequestType;
import jmine.tec.hydra.domain.specification.HydraSpecificationImpl;
import jmine.tec.utils.date.Interval;
import jmine.tec.utils.date.IntervalAggregator;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import bancosys.tec.persist.dao.BaseDAO;
import bancosys.tec.persist.dataset.HibernateRandomAccessResultSet;
import bancosys.tec.persist.hibernate.RestrictionsUtils;
import bancosys.tec.utils.date.Date;
import bancosys.tec.utils.date.DateUtils;
import bancosys.tec.utils.date.Timestamp;

/**
 * DAO de Hydra Request.
 * 
 * @author chovich
 */
public class HydraRequestDAO extends BaseDAO<HydraRequestImpl> {

    /**
     * Recupera as requisições de uma dada especificação agendada e um status, em ordem decrescente de data/hora de inicio (de mais atual a
     * mais antiga).
     * 
     * @param specification a especificação agendada
     * @param status o status.
     * @return as requisições.
     */
    public List<? extends HydraRequest> findByScheduledSpecificationStatus(HydraSpecificationImpl specification, StatusHydraRequest status) {
        Criteria criteria = this.createCriteria();
        RestrictionsUtils.addRestrictionEq(criteria, "specification", specification);
        RestrictionsUtils.addRestrictionEq(criteria, "status", status);
        criteria.addOrder(Order.desc("requestStart"));
        return this.executeQuery(criteria);
    }

    /**
     * Retorna hydra request por período, status, tipo, sistema e specification.
     * 
     * @param requestStart requestStart
     * @param requestEnd requestEnd
     * @param status status
     * @param requestType requestType
     * @param system system
     * @param specification specification
     * @return lista de hydra request.
     */
    public List<? extends HydraRequest> findByPeriodoStatusTipoSistema(Timestamp requestStart, Timestamp requestEnd,
            StatusHydraRequest status, HydraRequestType requestType, HydraSystem system, HydraSpecification specification) {
        Criteria criteria = this.createCriteria();
        RestrictionsUtils.addRestrictionGe(criteria, "requestStart", requestStart);
        RestrictionsUtils.addRestrictionLe(criteria, "requestEnd", requestEnd);
        RestrictionsUtils.addRestrictionEq(criteria, "status", status);
        RestrictionsUtils.addRestrictionEq(criteria, "requestType", requestType);
        RestrictionsUtils.addRestrictionEqId(criteria, "system.id", system);
        RestrictionsUtils.addRestrictionEqId(criteria, "specification.id", specification);
        criteria.addOrder(Order.desc("requestStart"));
        return this.executeQuery(criteria);
    }

    /**
     * Retorna uma lista de hydra request por período, status, tipo de requisição e sistema.
     * 
     * @param requestStart requestStart
     * @param requestEnd requestEnd
     * @param status {@link StatusHydraRequest}
     * @param requestType {@link HydraRequestType}
     * @param system {@link HydraSystem}
     * @return lista de hydra request a partir dos parâmetros informados.
     */
    public List<HydraRequestImpl> findByPeriodoStatusTipoSistema(Timestamp requestStart, Timestamp requestEnd, StatusHydraRequest status,
            HydraRequestType requestType, HydraSystem system) {
        Criteria criteria = this.createCriteria();
        RestrictionsUtils.addRestrictionGe(criteria, "requestStart", requestStart);
        RestrictionsUtils.addRestrictionLe(criteria, "requestEnd", requestEnd);
        RestrictionsUtils.addRestrictionEq(criteria, "status", status);
        RestrictionsUtils.addRestrictionEq(criteria, "requestType", requestType);
        RestrictionsUtils.addRestrictionEqId(criteria, "system.id", system);
        criteria.addOrder(Order.desc("requestStart"));
        return this.executeQuery(criteria);
    }

    /**
     * Retorna uma lista com todos os exceptionRecords que obedeçam os parâmetros especificados.
     * 
     * @param tsStart data-hora mínima para as exceções.
     * @param tsEnd data-hora máxima para as exceções.
     * @return uma lista de exceptinRecords.
     */
    public List<HydraRequestImpl> findRequests(Timestamp tsStart, Timestamp tsEnd) {
        Criteria criteria = this.createCriteria();
        criteria.addOrder(Order.desc("id"));
        RestrictionsUtils.addRestrictionGe(criteria, "requestStart", tsStart);
        RestrictionsUtils.addRestrictionLe(criteria, "requestEnd", tsEnd);
        return this.executeQuery(criteria);
    }

    /**
     * Retorna uma lista com todas as requisições feitas a partir dos parâmetros.
     * 
     * @param tsStart data/hora inicial
     * @param tsEnd data/hora final
     * @param requestType tipo da requisição
     * @param system sistema
     * @return uma lista com todas as requisições feitas a partir dos parâmetros.
     */
    public List<HydraRequestImpl> findByPeriodoTipoSistema(Timestamp tsStart, Timestamp tsEnd, HydraRequestType requestType,
            HydraSystem system) {
        Criteria criteria = this.createCriteria();
        RestrictionsUtils.addRestrictionGe(criteria, "requestStart", tsStart);
        RestrictionsUtils.addRestrictionLe(criteria, "requestEnd", tsEnd);
        RestrictionsUtils.addRestrictionEq(criteria, "requestType", requestType);
        RestrictionsUtils.addRestrictionEqId(criteria, "system.id", system);
        criteria.addOrder(Order.desc("requestStart"));
        return this.executeQuery(criteria);
    }

    /**
     * Buscas todas as requisições que executaram a especificação dada
     * 
     * @param specification specification
     * @return List de HydraRequestImpl
     */
    public List<HydraRequestImpl> findBySpecification(HydraSpecificationImpl specification) {
        Criteria criteria = this.createCriteria();
        criteria.add(Restrictions.eq("specification", specification));
        return this.executeQuery(criteria);
    }

    /**
     * Devolve bean com tempos do hydra.
     * 
     * @param daysToConsider dias a considerar para pesquisa dos tempos.
     * @param requestType tipo de requisicao (entrada ou saida).
     * @return bean com tempos do hydra.
     */
    public IntervalAggregator findLastHydraRequestTimes(int daysToConsider, HydraRequestType requestType) {
        String qs = "SELECT new jmine.tec.utils.date.Interval(requestStart, requestEnd) FROM HydraRequestImpl WHERE ";
        qs += " requestType = :type AND requestEnd >:ini";

        Query q = this.getSession().createQuery(qs);
        q.setParameter("type", requestType);
        q.setParameter("ini", DateUtils.createTimestampWithLastSecondOfTheDay(new Date().addDays(-daysToConsider - 1)));

        return new IntervalAggregator(new HibernateRandomAccessResultSet<Interval>(this.getSession(), q));
    }
}
